package simel;

import java.util.*;
import java.io.*;

public class JoeAgent implements Agent
{
    Simulator sim;
    boolean directions[];
    boolean initialMoveFinished[];
	int destinations[];
	int waiting[];
	int goingup[];
	int priority_up[];
	int priority_down[];
	int floormax;
	
	int hpt_count;
	int hpt[];
	int hpt_floor[];
	
    /** Note: Agents must have an empty constructor! **/
    public JoeAgent()
    {
    }
	
    /** init() will be called before any other method **/
    public void init(Simulator sim)
    {
        this.sim = sim;
		
        directions = new boolean[sim.getElevators().size()];
        initialMoveFinished = new boolean[sim.getElevators().size()];
		destinations = new int[sim.getElevators().size()];
		
        Random r = new Random(0);
		
		floormax = 0;
		
        // send each elevator to a random floor to start with.
        for (int i = 0; i < sim.getElevators().size(); i++) {
            Elevator e = sim.getElevators().get(i);
            if ( e.getFloorMax() > floormax ) floormax = e.getFloorMax();
			e.setDestination(e.getFloorMin() + r.nextInt(e.getFloorMax() - e.getFloorMin()));
            //e.setDestination(e.getFloorMin() + (e.getFloorMax() - e.getFloorMin())/2);
			destinations[i] = e.getDestination();
        }
		
		waiting = new int[floormax];
		goingup = new int[floormax];
		priority_up = new int[floormax];
		priority_down = new int[floormax];
		for ( int i = 0; i < floormax; i++ ) {
			waiting[i] = 0;
			goingup[i] = 0;
			priority_up[i] = 0;
			priority_down[i] = 0;
		}
		
		hpt_count = 4;
		hpt = new int[hpt_count];
		hpt_floor = new int[hpt_count];		
		for ( int i = 0; i < hpt_count; i++ ) {
			hpt[i] = 0;
			hpt_floor[i] = 0;
		}
    }
	
    // just send the elevators up and down and up and down.
    public void update(Simulator sim, ArrayList<Passenger> passengers, ArrayList<Elevator> elevators)
    {
		int dest_min = floormax;
		int dest_max = 0;
		
		// initialize the various state machines
		for ( int i = 0; i < floormax; i++ ) {
			waiting[i] = 0;
			goingup[i] = 0;
			priority_up[i] = 0;
			priority_down[i] = 0;
		}
		
		// initialize the high priority targets
		for ( int i = 0; i < hpt_count; i++ ) {
			hpt[i] = 0;
			hpt_floor[i] = 0;
		}
		
		// get passenger floor data
		for ( int i = 0; i < passengers.size(); i++ ) {
			Passenger p = passengers.get(i);
			//System.out.print(p.getTimeBorn() + " " + p.getFloorBorn() + " " + p.getDestination() + " " + p.getID() + " " + p.getFloor() );
			//System.out.println("  -  ");
			waiting[p.getFloor()-1] += 1;
			if ( p.getDestination() > p.getFloor() ) {
				goingup[p.getFloor()-1] += 1;
			} else {
				goingup[p.getFloor()-1] -= 1;
			}
			
			//System.out.println("TIME now: " + sim.time + " born: " + p.getTimeBorn());
			//System.out.println(Math.pow(sim.time - p.getTimeBorn(),2));
			if ( p.getFloor() < p.getDestination() ) { // going up
				priority_up[p.getFloor() - 1] += Math.pow(sim.time - p.getTimeBorn(),2);
			} else {
				priority_down[p.getFloor() - 1] += Math.pow(sim.time - p.getTimeBorn(),2);
			}
			
			if ( p.getDestination() > dest_max ) dest_max = p.getDestination();
			if ( p.getDestination() < dest_min ) dest_min = p.getDestination();
		}
		
		
		for ( int i = 0; i < floormax; i++ ) {
			int tmp_priority = priority_up[i] + priority_down[i];
			int tmp_floor = i + 1;
			if ( tmp_priority != 0 ) {
				for ( int j = 0; j < hpt_count; j++ ) {
					if ( tmp_priority > hpt[j] ) {
						int tp = hpt[j];
						int tf = hpt_floor[j];
						hpt[j] = tmp_priority;
						hpt_floor[j] = tmp_floor;
						tmp_priority = tp;
						tmp_floor = tf;
					}
				}
			}
		}
		
		/*
		 for ( int i = 0; i < hpt_count; i++ ) {
		 System.out.println( hpt[i] + " " + hpt_floor[i] );
		 }
		 System.out.println("--");
		 */
		
		//loop through the elevators
        for (int i = 0; i < elevators.size(); i++) {
            Elevator e = elevators.get(i);
			
			//System.out.println("Destination " + i + ": " + destinations[i]);
			
            // If the elevator still hasn't arrived at its initial starting place, just wait.
            if (!initialMoveFinished[i]) {
                if (e.getFloor() == e.getDestination()) {
                    initialMoveFinished[i] = true;
                    // we've arrived. fall through to usual logic below...
                } else {
                    // keep waiting
                    continue;
                }
            }
			
			boolean gotit = false;
			boolean setdest = false;
			if ( e.getFloor() == e.getDestination() ) {
				if ( waiting[e.getFloor()-1] == 0 ) {
					for ( int x = 0; x < hpt_count; x++ ) {
						if (  hpt_floor[x] >= e.getFloorMin() && hpt_floor[x] <= e.getFloorMax() ) {
							for ( int y = 0; y < i; y++ ) {
								Elevator ey = elevators.get(y);
								if ( ey.getDestination() != hpt_floor[x] && !( e.getFloorMin() == ey.getFloorMax() || e.getFloorMax() == e.getFloorMin() ) ) {
									setdest = true;
								} else {
									setdest = false;
								}
								
							}
							if ( setdest ) {
								e.setDestination( hpt_floor[x] );
								directions[i] = ( hpt_floor[x] > e.getFloor() ) ? true : false;
								gotit = true;
							}
						}
						setdest = false;
					}
				} else {
					if ( priority_up[e.getFloor()-1] >= priority_down[e.getFloor()-1] ) {
						if ( e.getFloor() < e.getFloorMax() ) {
							e.setDestination( e.getFloor() + 1 );
							directions[i] = true;
						} else {
							e.setDestination( e.getFloor() - 1 );
							directions[i] = false;
						}
					} else {
						if ( e.getFloor() > e.getFloorMin() ) {
							e.setDestination( e.getFloor() - 1 );
							directions[i] = false;
						} else {
							e.setDestination( e.getFloor() + 1 );
							directions[i] = true;
						}
					}
				}
			} else {
				gotit = true;
			}
			
			
			// going up
			if ( !gotit ) {
				if ( directions[i] ) { 
					// no one here
					if ( waiting[e.getFloor()-1] == 0 ) {
						// no one above
						int pass_up = 0;
						for ( int x = e.getFloor()+1; x < e.getFloorMax(); x++ ) {
							if ( waiting[x-1] > 0 ) pass_up++;
						}
						if ( pass_up == 0 ) {
							if ( e.getFloor() != e.getFloorMin() ) {
								directions[i] = false;
								e.setDestination( e.getFloor() - 1 );
							} else {
								e.setDestination( e.getFloor() + 1 );
							}
						} else {
							//e.setDestination( Math.min( dest_max, e.getFloorMax() ) );
							e.setDestination( e.getFloor() + 1 );
						}
					} else {
						if ( goingup[e.getFloor()-1] >= 0 ) {
							e.setDestination( e.getFloor() + 1 );
						} else {
							directions[i] = false;
							e.setDestination( e.getFloor() - 1 );
						}
					}
				} else { // going down
					if ( waiting[e.getFloor()-1] == 0 ) { // no one here
						int pass_down = 0;
						for ( int x = e.getFloorMin(); x < e.getFloor(); x++ ) {
							if ( waiting[x-1] > 0 ) pass_down++;
						}
						if ( pass_down == 0 ) { // no one below
							if ( e.getFloor() != e.getFloorMax() ) {
								directions[i] = true;
								e.setDestination( e.getFloor() + 1 );
							} else {
								e.setDestination( e.getFloor() - 1 );
							}
							
						} else {
							e.setDestination( e.getFloor() - 1 );
						}
					} else {
						if ( goingup[e.getFloor()-1] >= 0 ) {
							directions[i] = true;
							e.setDestination( e.getFloor() + 1 );
						} else {
							e.setDestination( e.getFloor() - 1 );
						}
					}
				}
				
				// if same floor and direction as previous elevator turn around
				for ( int x = 0; x < i; x++ ) {
					Elevator ex = elevators.get(x);
					if ( e.getFloor() == ex.getFloor() && directions[i] == directions[x] ) {
						if ( directions[i] ) {
							// go down
							e.setDestination( e.getFloor() - 1 );
							directions[i] = false;
						} else {
							//go up
							e.setDestination( e.getFloor() - 1 );
							directions[i] = true;
						}
						
						break;
					}
				}
				
				
				/*
				 if (directions[i])
				 e.setDestination(e.getFloor() + 1);
				 else
				 e.setDestination(e.getFloor() - 1);
				 */
			}
			
			// turn around at top or bottom if hasn't already
			if (e.getFloor() == e.getFloorMin() && !directions[i] ) {
				directions[i] = true;
				e.setDestination(e.getFloor() + 1);
			} else if (e.getFloor() == e.getFloorMax() && directions[i] ) {
				directions[i] = false;
				e.setDestination(e.getFloor() - 1);
			}
			
			destinations[i] = e.getDestination();
        }
    }
}
